﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;

namespace ETWebApi2
{
        public static class PredicateExtensionses
       {
        public static Expression<Func<T, bool>> True<T>() { return f => true; }
        public static Expression<Func<T, bool>> False<T>() { return f => false; }
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> exp_left, Expression<Func<T, bool>> exp_right)
        {
            var candidateExpr = Expression.Parameter(typeof(T), "candidate");
                var parameterReplacer = new ParameterReplacer(candidateExpr);
                var left = parameterReplacer.Replace(exp_left.Body); var right = parameterReplacer.Replace(exp_right.Body);
                var body = Expression.And(left, right);
                return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
        }
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> exp_left, Expression<Func<T, bool>> exp_right)
        {
            var candidateExpr = Expression.Parameter(typeof(T), "candidate");
                var parameterReplacer = new ParameterReplacer(candidateExpr);
                var left = parameterReplacer.Replace(exp_left.Body);
                var right = parameterReplacer.Replace(exp_right.Body);
                var body = Expression.Or(left, right);
                return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
        }
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName, bool ascending) where T : class
        {
            Type type = typeof(T);
            PropertyInfo property = type.GetProperty(propertyName);
            if (property == null) throw new ArgumentException("propertyName", "Not Exist");
            ParameterExpression param = Expression.Parameter(type, "p");
            Expression propertyAccessExpression = Expression.MakeMemberAccess(param, property);
            LambdaExpression orderByExpression = Expression.Lambda(propertyAccessExpression, param);
            string methodName = ascending ? "OrderBy" : "OrderByDescending";
            MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName, new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExpression));
            return source.Provider.CreateQuery<T>(resultExp);
        }
    /// <summary>   /// 统一ParameterExpression 
    /// </summary>   
    internal class ParameterReplacer : ExpressionVisitor
        {
            public ParameterReplacer(ParameterExpression paramExpr)
            {
                this.ParameterExpression = paramExpr;
            }
            public ParameterExpression ParameterExpression { get; private set; }
            public Expression Replace(Expression expr) {return this.Visit(expr); }
            protected override Expression VisitParameter(ParameterExpression p){ return this.ParameterExpression;  }
        } 
    }
}
